using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.Modeling;

namespace DslCrossModelFramework.ModelRelationship {

    /// <summary>
    /// Filters a collection of model elements to only those matching the generic type argument
    /// </summary>
    /// <typeparam name="TModelElement">The type of the model element to include.</typeparam>
    public class ModelElementTypeFilter<TModelElement> : ModelElementFilterBase {
        public override bool IsValid(ModelElement modelElement) {
            return modelElement.GetType() == typeof(TModelElement) || modelElement.GetType().IsSubclassOf(typeof(TModelElement));
        }
    }

    /// <summary>
    /// Filters a collection of model elements to only those not matching the generic type argument
    /// </summary>
    /// <typeparam name="TModelElementToExclude">The type of the model element to exclude.</typeparam>
    public class ModelElementExcludeTypeFilter<TModelElementToExclude> : ModelElementFilterBase {
        public override bool IsValid(ModelElement modelElement) {
            return modelElement.GetType() != typeof(TModelElementToExclude) &&
            !modelElement.GetType().IsSubclassOf(typeof (TModelElementToExclude));
        }
    }

    /// <summary>
    /// Filters a collection of model elements to only those matching the types specified in the constructor
    /// </summary>
    public class ModelElementTypeFilter : ModelElementFilterBase {

        /// <summary>
        /// Initializes a new instance of the <see cref="ModelElementTypeFilter"/> class.
        /// </summary>
        /// <param name="domainClasses">The domain classes.</param>
        public ModelElementTypeFilter(IEnumerable<DomainClassInfo> domainClasses) {
            List<Type> tempDomainClassTypes = new List<Type>();
            foreach (DomainClassInfo info in domainClasses) {
                tempDomainClassTypes.Add(info.ImplementationClass);
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ModelElementTypeFilter"/> class.
        /// </summary>
        /// <param name="domainClassTypes">The domain class types.</param>
        public ModelElementTypeFilter(IEnumerable<Type> domainClassTypes) {
            this.domainClassTypes = domainClassTypes;
        }

        private readonly IEnumerable<Type> domainClassTypes;


        public override bool IsValid(ModelElement modelElement) {
            foreach (Type type in domainClassTypes) {
                if (modelElement.GetType() == type || modelElement.GetType().IsSubclassOf(type)) {
                    return true;
                }
            }
            return false;
        }
    }

    /// <summary>
    /// Filters a collection of model elements to only those matching the types specified in the constructor
    /// </summary>
    public class ModelElementExcludeTypeFilter : ModelElementFilterBase {

        /// <summary>
        /// Initializes a new instance of the <see cref="ModelElementExcludeTypeFilter"/> class.
        /// </summary>
        /// <param name="domainClasses">The domain classes.</param>
        public ModelElementExcludeTypeFilter(IEnumerable<DomainClassInfo> domainClasses) {
            List<Type> tempDomainClassTypes = new List<Type>();
            foreach (DomainClassInfo info in domainClasses) {
                tempDomainClassTypes.Add(info.ImplementationClass);
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ModelElementExcludeTypeFilter"/> class.
        /// </summary>
        /// <param name="domainClassTypes">The domain class types.</param>
        public ModelElementExcludeTypeFilter(IEnumerable<Type> domainClassTypes) {
            this.domainClassTypes = domainClassTypes;
        }

        private readonly IEnumerable<Type> domainClassTypes;


        public override bool IsValid(ModelElement modelElement) {
            foreach (Type type in domainClassTypes) {
                if (modelElement.GetType() == type || modelElement.GetType().IsSubclassOf(type)) {
                    return false;
                }
            }
            return true;
        }
    }
}